package ee.parameterConverter.guice;

import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

import javax.inject.Inject;

import com.google.inject.Binding;
import com.google.inject.Injector;
import com.google.inject.TypeLiteral;
import com.google.inject.util.Types;

import ee.metadataUtils.ClassUtils;
import ee.parameterConverter.Converter;
import ee.parameterConverter.ConverterResolver;
import ee.parameterConverter.GenericStringConverter;
import ee.parameterConverter.TypeKind;
import ee.parameterConverter.TypeKindResolver;
import ee.parameterConverter.impl.ConverterResolverImpl.StringConverterWrapper;

public class GuiceConverterResolver implements ConverterResolver {

	private Injector _injector;
	private TypeKindResolver _typeKindResolver;
	private GenericStringConverter _genericStringConverter;

	@Inject
	public GuiceConverterResolver(Injector injector, TypeKindResolver typeKindResolver, GenericStringConverter genericStringConverter) {
		_injector = injector;
		_typeKindResolver = typeKindResolver;
		_genericStringConverter = genericStringConverter;
	}
	
	@Override
	public Converter<Map<String, String[]>, ?> getParameterConverter(Type type) {
		type = _getWrapperIfPrimitive(type);
		
		TypeLiteral<?> typeLiteral = TypeLiteral.get(Types.newParameterizedType(Converter.class, Types.newParameterizedType(Map.class, String.class, String[].class), type));
		
		List<?> bindings = _injector.findBindingsByType(typeLiteral);
		
		if (bindings.size() > 0)
		{
			@SuppressWarnings("unchecked")
			Converter<Map<String, String[]>, ?> converter = (Converter<Map<String, String[]>, ?>) ((Binding<?>) bindings.get(0)).getProvider().get();
			return converter;
		}
		
		return null;
	}

	@Override
	public Converter<String[], ?> getStringArrayConverter(Type type) {
		type = _getWrapperIfPrimitive(type);
		
		TypeLiteral<?> typeLiteral = TypeLiteral.get(Types.newParameterizedType(Converter.class, String[].class, type));
		
		List<?> bindings = _injector.findBindingsByType(typeLiteral);
		
		if (bindings.size() > 0)
		{
			@SuppressWarnings("unchecked")
			Converter<String[], ?> converter = (Converter<String[], ?>) ((Binding<?>) bindings.get(0)).getProvider().get();
			return converter;
		}
		
		//see if we can use a generic converter
		TypeKind typeKind = _typeKindResolver.resolve(type);
		
		Converter<String[], ?> converter;
		
		if (typeKind.equals(TypeKind.Simple))
		{
			converter = _genericStringConverter.getStringArrayConverter(type);
		} else
		{
			converter = null;
		}
		
		Converter<String, ?> stringConverter = _getStringConverter(type);
		
		// check if we can use a string converter
		if (stringConverter != null)
		{
			converter = new StringConverterWrapper(stringConverter, converter);
		}
		
		return converter;
	}

	@Override
	public Converter<String, ?> getStringConverter(Type type) {
		type = _getWrapperIfPrimitive(type);
		
		Converter<String, ?> converter = _getStringConverter(type);
		
		if (converter != null)
		{
			return converter;
		}

		//see if we can use a generic converter
		TypeKind typeKind = _typeKindResolver.resolve(type);
		
		if (typeKind.equals(TypeKind.Simple))
		{
			return _genericStringConverter.getStringConverter(type);
		}
		
		return null;
	}

	private Converter<String, ?> _getStringConverter(Type type)
	{
		TypeLiteral<?> typeLiteral = TypeLiteral.get(Types.newParameterizedType(Converter.class, String.class, type));
		
		List<?> bindings = _injector.findBindingsByType(typeLiteral);
		
		if (bindings.size() > 0)
		{
			@SuppressWarnings("unchecked")
			Converter<String, ?> converter = (Converter<String, ?>) ((Binding<?>) bindings.get(0)).getProvider().get();
			return converter;
		}
		
		return null;
	}
	
	private Type _getWrapperIfPrimitive(Type type)
	{
		if (type instanceof Class && ClassUtils.isPrimitive((Class<?>) type))
		{
			type = ClassUtils.getWrapper((Class<?>) type);
		}

		return type;
	}
}
